Ajax Based Login with ZK and Spring Security System
Henri Chen, Principal Engineer, Potix Corporation
- Ashish Dasnurkar, Engineer, Potix Corporation
October 13, 2008
- Updated April 13, 2010 to make it work with ZK Spring integration library 3.0RC
Applicable to ZK Spring Integration Library 3.0RC (zkspring-core.jar and zkspring-security.jar) or later.
- Applicable to ZK 3.0.9 and later.
- Applicable to ZK 3.5.1 and later.
- Applicable to ZK 5.0 and later.
- Applicable to Spring Security 3.0+
Introduction
This is the second article in a series regarding how to make Spring Security 3.0 work with ZK Ajax framework. In the previous article, we only discussed about protecting traditional page-based Web applications. In this article, we will focus on how to deal with Ajax kind of application.
Like the previous article, I will focus on the steps to "make it work". If you are interested in the behind-the-scene things, you are welcome to check the source codes :-).
Demo
As you can see in the demo, the login window is popped up automatically rather than change to a separate login page. The end user does not have to leave the working "Accounts" page so make it more intuitive and interactive.
The Example
This is the same example as used in the previous article. It is originated from the tutorial sample(spring-security-3.0.2.RELEASE\dist\spring-security-samples-tutorial-3.0.2.RELEASE.war) provided by Spring Security 3.0. Basically I rewrote only the /WEB-INF/jsp/listAccounts.zul to handle the onClick event of -$20, -$5, +$5, and +$20 buttons. Whenever the end user presses any of these buttons, an associated onClick event listener is called and executed. The ZK event processing interceptor then intercept such event and pop up the login window if necessary.
A Minimal <zk-event> Configuration
ZK adopts the Spring namespace configuration mechanism so all you need to do to enable the ZK event processing security is as simple as following:
/WEB-INF/applicationContext-security.xml
<!--
- Spring namespace-based configuration
-->
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:zksp="http://www.zkoss.org/2008/zkspring/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd
http://www.zkoss.org/2008/zkspring/security http://www.zkoss.org/2008/zkspring/security/zkspring-security.xsd">
<http ...>
...
</http>
...
<!--
- Secure the ZK event processing per the event name and ZK component path pattern
-->
<zksp:zk-event login-template-close-delay="5">
<zksp:intercept-event event="onClick" path="//**/btn_*" access="ROLE_TELLER"/>
<zksp:intercept-event path="/**" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
</zksp:zk-event>
</beans:beans>
- xmlns:zksp="http://www.zkoss.org/2008/zkspring/security" tells the Spring Security engine that we will use the ZK Spring namespace configuration and define the name space to zksp.
- http://www.zkoss.org/2008/zkspring/security http://www.zkoss.org/2008/zkspring/security/zkspring-security.xsd tells the Spring Security engine where to find such ZK Spring namespace configuration schema.
- <zksp:zk-event> tells the Spring Security engine we want to secure ZK event processing. This will configure the necessary listeners , filters, and Spring beans automatically. In this example, login-template-close-delay="5" tells the ZK to CLOSE the login window automatically in five(5) seconds if login successfully; zero(0) means close the login window immediately; and a negative value means wait for users operation.
- <zksp:intercept-event event="onClick" path="//**/btn_*" access="ROLE_TELLER"/> tells the Spring Security engine which ZK event and components we want to secure. In this example, it says that any onClick event that is fired to the ZK components whose id starts with btn_ will be checked against the ROLE_TELLER authority.
- <zksp:intercept-event path="/**" access="IS_AUTHENTICATED_ANONYMOUSLY"/> tells that all anonymous users can access to all events and all components.
- Note that the authorization follows the convention of the Spring Security system. That is, the <zksp:intercept-event> rules are checked one by one from top to bottom. As long as one rule is matched, the system will just stop there and leave, so more specific rule shall be put on upper place.
- Also, we used to ask you to configure the WEB-INF/zk.xml to handle the ZK event thread issue(see the previous article). You do NOT have to now since the <zksp:zk-event> tag will do those configuration for you automatically.
- Do remember to deploy the new ZK Spring Integration library -- zkspring.jar in WEB-INF/lib because all these magics are done by this new jar file.
Rewrite /WEB-INF/jsp/listAccounts.zul
This is the page that I demonstrate how to secure ZK event processing. It mainly lists all accounts and end users can change the balance of each account by pressing the four buttons to minus or plus the balance. In its original implementation, when the end user presses a button, a request is posted to the /post.html action page and calls the bankServer.post() method(in bigbank.BankService class) to change the account balance; then it refresh the whole page to list all accounts again.
In this use case, the bankServer.post() method is protected by Spring Security system. If an unauthenticated user presses the button, in the original implementation, the Spring Security will show a separate login page(and leave the current Accounts page) and ask the user to login. After successful login, the security system redirect browser to the original page.
Here I rewrote this page and make it Ajaxified. When the end user presses the button, the onClick event listener is called and ONLY the changed account balance is updated. There is no need to refresh the whole page. Then how does ZK ask the end user to login without leave the current page? It pops up a login window and user can login in the popped up login window as you have seen in the demo video.
/WEB-INF/jsp/listAccounts.zul
<?variable-resolver class="org.zkoss.spring.DelegatingVariableResolver"?>
<zk>
<window title="Accouts" border="normal" width="500px">
<zscript><![CDATA[
void adjBalance(Button btn) {
double bal = Double.parseDouble((String)btn.getAttribute("bal"));
//get the account object
bigbank.Account a = bankService.readAccount(btn.getAttribute("aid"));
//change the account balance
bankService.post(a, bal);
//update the account balance on the browser
btn.getFellow("bal_"+a.getId()).setValue(""+a.getBalance());
}
]]>
</zscript>
<grid>
<rows>
<row forEach="${accounts}">
<label value="${each.id}"/>
<label value="${each.holder}"/>
<label id="bal_${each.id}" value="${each.balance}"/>
<button id="btn_m20_${each.id}" label="-$20" onClick="adjBalance(self)">
<custom-attributes aid="${each.id}" bal="-20"/>
</button>
<button id="btn_m5_${each.id}" label="-$5" onClick="adjBalance(self)">
<custom-attributes aid="${each.id}" bal="-5"/>
</button>
<button id="btn_p5_${each.id}" label="+$5" onClick="adjBalance(self)">
<custom-attributes aid="${each.id}" bal="5"/>
</button>
<button id="btn_p20_${each.id}" label="+$20" onClick="adjBalance(self)">
<custom-attributes aid="${each.id}" bal="+20"/>
</button>
</row>
</rows>
</grid>
</window>
<button label="Home" href="/index.zul"/>
<button label="Logout" href="/j_spring_security_logout"/>
Here I brief a little bit the process sequence of this use case:
- The end user clicks a button and the onClick event is fired to the button.
- The ZK event processing security system checks whether the event is protected(<intercept-event>) from top to bottom.
- If the event matches the specified event("onClick") and the component id matches the specified path(//**/btn_*, starts with btn_), the ZK event processing has to be secured.
- Check if the user is authenticated and with granted authority (ROLE_TELLER).
- Well, the user is not authenticated, pop up the login window.
- The end user keys in proper user id and password and submit the login form.
- The Spring Security system processes the authentication and succeed.
- The login window shows the login successful page.
- Since the system is configured to close the login window in 5 seconds after the user logining successfully, it starts to count down and finally close itself.
- The end user clicks the button again and this time it passes the authentication check and proceeds to adjBalance() method.
- Inside the adjBalance method, we call bankService.post() method to update the account balance. Note we can refer the bankService Spring bean directly because we have specified in the page to use the Spring bean resolver (<?variable-resolver class="org.zkoss.spring.DelegatingVariableResolver"?>.
- Then we update the account balance label (without "refresh the whole page").
Summary
We have demonstrated how easy it is to secure the ZK event processing. All you have to do is declaring <zk-event> configuration tag in Spring's security configuration file. If you are not familiar with ZK event, it is OK. You can still secure the service layer methods by the Spring's @Secured annotation. As long as the method is called inside ZK event listener, the ZK event processing security system will handle it automatically.
Making ZK and Spring Security 3.0 works together is just the first step. The ZK Team will continue on making integrating ZK and Spring Framework as easy as possible. Currently ZK can refer the Spring bean easily with the variable resolver mechanism but the Spring bean has not yet been able to "inject" ZK components in any way. We will focus on solving this issue in the future and we welcome your feedback and suggestions so we can make ZK Spring Integration better.
Download
- Download the ZK Spring Integration Library 1.1.0 example codes (.war file)
- Download the ZK Spring Integration Library 3.0RC example codes(.war file).
Copyright © Potix Corporation. This article is licensed under GNU Free Documentation License. |